package com.apes.framework.plugin.fsm;

import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.ApesBean;
import com.apes.framework.jpa.DefaultPersistListener;
import com.apes.framework.jpa.Model;
import com.apes.framework.jpa.comment.Comment;
import com.apes.framework.jpa.generator.ApesGenerator;
import com.apes.framework.plugin.fsm.schedule.WorkSchedule;
import com.apes.framework.plugin.soe.annotations.State;
import com.apes.framework.plugin.soe.annotations.States;
import com.apes.framework.plugin.soe.metadata.BusinessAttributeValue;
import com.apes.framework.util.JpaUtil;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.springframework.util.StringUtils;
import org.squirrelframework.foundation.fsm.StateCompositeType;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 功能：状态
 *
 * @author xul
 * @create 2020-08-27 11:37
 */
@Getter
@Setter
@SelectBeforeUpdate
@DynamicInsert
@DynamicUpdate
@Entity
@EntityListeners(DefaultPersistListener.class)
@Table(name = FsmState.CONST_TABLE_NAME)
public class FsmState extends Model {
    public static final String CONST_TABLE_NAME = "fsm_state";

    @Id
    @Column(name = "id", length = 32)
    @ApesGenerator(code = CONST_TABLE_NAME, name = "状态流：状态")
    @Comment("编码")
    private String id;

    @Column(name = "name", nullable = false, length = 60)
    @Comment("状态名称")
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "fsm_flow_id", referencedColumnName = "id", nullable = false)
    @Comment("状态流")
    private FsmFlow fsmFlow;

    @States(name = "复合类型", values = {
            @State(id = "sequential", name = "顺序"),
            @State(id = "parallel", name = "并行")})
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "composite_type_id", referencedColumnName = "id", nullable = false)
    private BusinessAttributeValue compositeType;

    public StateCompositeType convertCompositeType() {
        if (compositeType == null || compositeType.getCode().equals("sequential")) {
            return StateCompositeType.SEQUENTIAL;
        }
        return StateCompositeType.PARALLEL;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent", referencedColumnName = "id")
    @Comment("上级节点")
    private FsmState parent;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "child_fsm_flow_id", referencedColumnName = "id")
    @Comment("子状态流")
    private FsmFlow childFsmFlow;

    @Column(name = "started", nullable = false, columnDefinition = "number(1,0) default 0")
    @Comment("是否启动状态")
    private boolean started = false;

    public void setStarted(boolean started) {
        if (started && this.finish) {
            throw new RuntimeException("一个状态不允许同时为开始和结束状态！");
        }
        this.started = started;
    }

    @Column(name = "finish", nullable = false, columnDefinition = "number(1,0) default 0")
    @Comment("是否结束状态")
    private boolean finish = false;

    public void setFinish(boolean finish) {
        if (this.started && finish) {
            throw new RuntimeException("一个状态不允许同时为开始和结束状态！");
        }
        this.finish = finish;
    }

    @Column(name = "suspend", nullable = false, columnDefinition = "number(1,0) default 0")
    @Comment("是否挂起状态")
    private boolean suspend = false;

    @Column(name = "duration", length = 32)
    @Comment("考核时长（格式：hh:mm）")
    private String duration;

    @Column(name = "base_value", length = 120)
    @Comment("考核基值字段")
    private String baseValue;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "work_schedule_id", referencedColumnName = "id")
    @Comment("考核规则")
    private WorkSchedule workSchedule;

    @Column(name = "state_sort", length = 3)
    @Comment("排序字段")
    private Integer stateSort;

    @OneToMany(mappedBy = "to", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<FsmTransit> transitions = new ArrayList<>();

    public List<FsmTransit> getTransitions() {
        if (transitions == null) transitions = new ArrayList<>();
        for (FsmTransit transit : transitions) {
            transit.setTo(this);
        }
        return transitions;
    }

    @OneToMany(mappedBy = "state", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<FsmTask> tasks = new ArrayList<>();

    public List<FsmTask> getTasks() {
        if (tasks == null) tasks = new ArrayList<>();
        for (FsmTask task : tasks) {
            task.setState(this);
        }
        return tasks;
    }

    public boolean parallelState() {
        return this.compositeType.getCode() == "parallel";
    }

    public boolean hasChildStates() {
        List<FsmState> childStates = findChildStates();
        return childStates != null && childStates.size() > 0;
    }

    private List<FsmState> findChildStates() {
        if(StringUtils.isEmpty(this.getId())) return null;
        FsmStateRepository repository = SpringManager.getBean(FsmStateRepository.class);
        return repository.findAllByParent(this);
    }

    public void verify() {
        if (this.isFinish()) {
            if (this.parallelState()) {
                throw new IllegalStateException("Final state cannot be parallel state.");
            }

            if (this.hasChildStates()) {
                throw new IllegalStateException("Final state cannot have child states.");
            }
        }

        if (this == parent) {
            throw new IllegalArgumentException("parent state cannot be state itself.");
        } else if (parent != null) { /**判断有向图中是否有环**/

        }
    }

    /**
     * 考核时长（单位：分钟）
     **/
    public int calcDuration() {
        int result = 0;
        if(StringUtils.isEmpty(this.duration)) return result;
        String[] segment = this.duration.split(":");
        for (int i = 0; i < segment.length; i++) {
            if (i == 0) {
                result += Integer.valueOf(segment[i]) * 60;
            } else if (i == 1) {
                result += Integer.valueOf(segment[i]);
            }
        }
        return result;
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != JpaUtil.getDomainClass((ApesBean) o)) return false;
        FsmState fsmState = (FsmState) o;
        return Objects.equals(id, fsmState.getId());
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public String toString() {
        return name;
    }

    //相关指标：时效、责任人。。。
}
